#ifndef __BASE_H__
#define __BASE_H__
#include <iostream>
#include <vector>
#include <map>
#include <atomic>
#include <queue>
#include <mutex>
#include <wtypes.h>
#include <atldbcli.h>
#include <timeapi.h>
#include <windows.h>
#include <comutil.h>
using namespace std;
using namespace ATL;

#include "convert_argb.h"
#include "rotate_argb.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libswscale/swscale.h"

#include "get-graphics-offsets/get-graphics-offsets.h"
#include "hook-helpers.h"
#include "pipe.h"
#include "graphics-hook-info.h"
#include "app-helpers.h"
#include "inject-library.h"
#ifdef __cplusplus
}
#endif // __cplusplus

#define CHECK_F(hr)  if (FAILED(hr)) \
						 {				 \
							return;	\
						 }

#define CHECK_F_RETURN(hr) if (FAILED(hr)) \
						 {				 \
							return hr;	\
						 }


#define CHECK_S_BREAK(hr) if (SUCCEEDED(hr)) \
						 {				 \
							break;	\
						 }

#define SAFE_FREE_AVFRAME(p) if (p){ \
							av_frame_free(&p); \
							p = NULL; \
							}

#define SAFE_FREE_POINT(p) if (p){ \
							delete p; \
							p = NULL; \
							}

#define SAFE_FREE_ARRAY(p) if (p){ \
							delete[] p;	\
							p = NULL; \
							}

#define  SAFE_RELEASE_DC(hwnd,hdc)  if(hdc){::ReleaseDC(hwnd,hdc);hdc=NULL;}
#define  SAFE_DELETE_DC(hdc)  if(hdc){::DeleteDC(hdc);hdc=NULL;}
#define  SAFE_DELETE_OBJECT(obj)  if(obj){::DeleteObject(obj);obj=NULL;}

#define AutoLock std::lock_guard<mutex> 
#define maxSize 1024
#define devicesNum 5
#define default_index 0
#define maxCaptureSize 10
#define pts_x 40

enum renderType
{
	GDI_PLUS = 1,
	D3DX_RENDER = 2,
	D3D_RENDER = 3
};

enum muxHandleType
{
	MUX_NONE = 0,
	MUX_AUDIO = 1,
	MUX_VIDEO = 2,
	MUX_ALL = 3
};

enum recType
{
	REC_NONE = 0,
	REC_SYS = 1,
	REC_MIC = 2,
	REC_ALL = 3
};

enum deviceType
{
	DEVICE_NONE = 0,
	DEVICE_SYS = 1,
	DEVICE_MIC = 2,
	DEVICE_ALL = 3,
};

enum crapType
{
	DESKTOP = 1,
	CAMERA = 2,
	WINDOW = 4,
	GAME = 8
};

enum ctxType
{
	VOICE = 0,
	FORMAT = 1,
	SUCCESS = 2,
	MIX = 3,
	CONVERT = 4
};


class ControlParam
{

public:
	renderType render_type;
	muxHandleType mux_type;
	recType rec_type;
	deviceType device_type;
	crapType crap_type;
	bool USE_RTMP;
	string video_url;
	int scale;
	HWND h_capture;

	int renderw;
	int renderh;
	HWND hwnd;

	int cameraw;
	int camerah;
	HWND camera_hwnd;

	ControlParam()
	{
	}

	void operator=(const ControlParam& param) 
	{
		this->render_type = param.render_type;
		this->mux_type = param.mux_type;
		this->rec_type = param.rec_type;
		this->device_type = param.device_type;
		this->crap_type = param.crap_type;
		this->USE_RTMP = param.USE_RTMP;
		this->scale = param.scale;
		this->video_url = param.video_url;
		this->renderw = param.renderw;
		this->renderh = param.renderh;
		this->hwnd = param.hwnd;
		this->h_capture = param.h_capture;
		this->cameraw = param.cameraw;
		this->camerah = param.camerah;
		this->camera_hwnd = param.camera_hwnd;
	}
};
extern ControlParam g_param;


class DataPacket
{
public:
	uint8_t* pData;
	UINT usize;
	UINT used_pos;

	DataPacket(UINT size)
	{
		pData = new uint8_t[size];
		memset(pData, 0, size);
		usize = size;
		used_pos = 0;
	}

	DataPacket(uint8_t* p, UINT size)
	{
		pData = p;
		usize = size;
		used_pos = 0;
	}
	~DataPacket()
	{
		if (pData)
			delete[] pData;
	}
};

//
class SwrConvert
{
private:
	int m_inputSamples;
	int m_outSamples;
	SwrContext* m_pSwrCtx;
public:
	SwrConvert();
	void SetParam(
		AVSampleFormat inputFormat,
		AVSampleFormat outFormat,
		int inputChannel,
		int outChannel,
		int inputSamples,
		int outSamples);
	~SwrConvert();
	DataPacket* Convert(DataPacket* pSrcData, int size);
};
//


//
class FormatParam
{
public:
	AVSampleFormat m_captureFormat;
	int m_captureChannel;
	int m_captureSamples;
	
	AVSampleFormat m_outFormat = AV_SAMPLE_FMT_S16;
	int m_outChannel = 2;
	int m_outSamples = 44100;

	bool NeedConvert()
	{
		return (m_captureFormat != m_outFormat) || (m_captureChannel != m_outChannel) || (m_captureSamples != m_outSamples);
	}

	int GetEncodeOutSize()
	{
		return m_outChannel * m_outSamples * 2 / 100;
	}

	int GetConvertSize()
	{
		int result = m_captureChannel * m_captureSamples / 100;
		if (m_captureFormat == AV_SAMPLE_FMT_FLT)
			result *= 4;
		else if (m_captureFormat == AV_SAMPLE_FMT_S16)
			result *= 2;
		return result;
	}
};

char* ws2str(wstring& wstr);
wchar_t* s2wstr(string& str);

extern mutex ctx_delete_mutex;
extern mutex wave_mutex;
extern uint8_t* pWave;
#endif
